﻿using JX.Enrollment.Entity;
using JX.Enrollment.Web.Implementation.Core;
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Data.Entity.Core.Objects;
using System.Data.Entity.Core.Objects.DataClasses;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

namespace JX.Enrollment.Web.Implementation
{
    /// <summary>
    /// Base Data Access implementing Core methods
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class BaseDA<T> : IBaseDA<T> where T : class
    {
        public object DatabaseContext
        {
            get
            {
                return EntityContext;
            }
            set
            {
                EntityContext = (DbEntities)value;
            }
        }

        public DbEntities _EntityContext;

        public DbEntities EntityContext
        {
            get
            {
                if (_EntityContext == null)
                    _EntityContext = new DbEntities();
                return _EntityContext;
            }
            set
            {
                _EntityContext = value;
            }
        }

        public virtual bool Insert(T t)
        {
            EntityContext.Set<T>().Add(t);
            bool result = EntityContext.SaveChanges() > 0;

            return result;
        }

        public T GetById(object id)
        {


            return null;
        }

        private string GetKeyPropertyName(Type type)
        {
            foreach (PropertyInfo pinfo in type.GetProperties())
                foreach (Attribute attr in pinfo.GetCustomAttributes(true))
                    if (attr is EdmScalarPropertyAttribute)
                        if ((attr as EdmScalarPropertyAttribute).EntityKeyProperty)
                            return pinfo.Name;

            throw new ApplicationException("No Entity Key property for " + type.Name);
        }

        private string GetEntitySetName(T t)
        {
            string name = t.GetType().Name;

            return name;
        }

        public virtual int InsertReturnId(T t)
        {
            int result = 0;

            return result;
        }

        public virtual long InsertReturnLongId(T t)
        {
            long result = 0;

            return result;
        }

        public virtual bool Update(T t)
        {
            bool result = false;

            //save modified entity using new Context
            using (var dbContext = new DbEntities())
            {
                dbContext.Entry(t).State = System.Data.Entity.EntityState.Modified;
                result = dbContext.SaveChanges() > 0;
            }

            return result;
        }

        public virtual bool Delete(T t)
        {
            EntityContext.Set<T>().Remove(t);
            bool result = EntityContext.SaveChanges() > 0;

            return result;
        }

        public virtual IEnumerable<T> GetAll()
        {
            IEnumerable<T> result = EntityContext.Set<T>().ToList<T>();

            return result;
        }

        public virtual IList<T> GetAllAsList()
        {
            IList<T> result = GetAll().ToList<T>();

            return result;
        }

        public virtual HashSet<T> GetAllAsHashSet()
        {
            HashSet<T> result = new HashSet<T>();
            foreach (T t in GetAll())
                result.Add(t);

            return result;
        }

        private PropertyInfo GetObjectSetPropertyInfo()
        {
            Type type = EntityContext.GetType();
            string name = typeof(T).Name;
            PropertyInfo result = type.GetProperty(name);

            if (result == null)
                throw new ApplicationException(string.Format("Invalid {0} in context with ObjectSet<> return type!", name));

            return result;
        }

        public bool DeleteAll()
        {
            DbConnection conn = EntityContext.Database.Connection;

            DbCommand command = conn.CreateCommand();
            command.CommandText = "DELETE FROM " + typeof(T).Name;
            bool result = command.ExecuteNonQuery() == 0;

            return result;
        }

        public IEnumerable<T> Where(Expression<Func<T, bool>> expression)
        {
            //ObjectSet<T> objectSet = GetObjectSetPropertyInfo().GetValue(EntityContext, null) as ObjectSet<T>;
            IEnumerable<T> result = EntityContext.Set<T>().Where(expression);//objectSet.Where(expression);

            return result;
        }

        public IList<T> WhereAsList(Expression<Func<T, bool>> expression)
        {
            ObjectSet<T> objectSet = GetObjectSetPropertyInfo().GetValue(EntityContext, null) as ObjectSet<T>;
            IList<T> result = objectSet.Where(expression).ToList<T>();

            return result;
        }

        public HashSet<T> WhereAsHashSet(Expression<Func<T, bool>> expression)
        {
            ObjectSet<T> objectSet = GetObjectSetPropertyInfo().GetValue(EntityContext, null) as ObjectSet<T>;

            HashSet<T> result = new HashSet<T>();
            foreach (T obj in objectSet.Where(expression))
                result.Add(obj);

            return result;
        }

        public int Count()
        {
            ObjectSet<T> objectSet = GetObjectSetPropertyInfo().GetValue(EntityContext, null) as ObjectSet<T>;

            int result = objectSet.Count<T>();

            return result;
        }

        public int Count(Expression<Func<T, bool>> expression)
        {
            ObjectSet<T> objectSet = GetObjectSetPropertyInfo().GetValue(EntityContext, null) as ObjectSet<T>;

            int result = objectSet.Count<T>(expression);

            return result;
        }

        public bool IsEmpty()
        {
            bool result = this.Count() > 0;

            return result;
        }

        public bool IsEmpty(Expression<Func<T, bool>> expression)
        {
            bool result = this.Count(expression) > 0;

            return result;
        }
    }
}